package com.rc.saas.tenant.common.redis;

import com.rc.saas.tenant.common.utils.SerializeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public class RedisDaoImpl implements RedisDao {

    @Autowired
    protected RedisTemplate redisTemplate;

    @Override
    public Object getSession(final String keyValue) {
        return redisTemplate.execute(new RedisCallback<Object>() {

            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] key = redisTemplate.getStringSerializer().serialize(keyValue);
                if (connection.exists(key)) {
                    byte[] value = connection.get(key);
                    return SerializeUtil.unserialize(value);
                }
                return null;
            }
        });

    }

    @Override
    public void putSession(final String key, final Object value) {
        redisTemplate.execute(new RedisCallback<Object>() {
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                connection.set(redisTemplate.getStringSerializer().serialize(key), SerializeUtil.serialize(value));
                //connection.set(redisTemplate.getStringSerializer().serialize(key), value);
                return null;
            }
        });
    }

    @Override
    public void putListSession(final String key, final Object value) {
        redisTemplate.opsForList().leftPush(key, value);
    }

    @Override
    public List getListSession(final String keyValue) {
        final Long listSize = redisTemplate.opsForList().size(keyValue);
        List list = redisTemplate.opsForList().range(keyValue, 0, listSize);
        return list;
    }

    @Override
    public void popListSession(final String keyValue) {
        final Long listSize = redisTemplate.opsForList().size(keyValue);
        for (int i = 0; i < listSize; i++) {
            redisTemplate.opsForList().leftPop(keyValue);
        }
    }

    @Override
    public void setListSession(final String key, final int index, final Object value) {
        redisTemplate.opsForList().set(key, index, value);
    }

    @Override
    public Long listSessionSize(final String key) {
        return redisTemplate.opsForList().size(key);
    }

    @Override
    public void removeListSession(final String key, final long index, final Object value) {
        redisTemplate.opsForList().remove(key, index, value);
    }

    @Override
    public void putHashAllSession(final String key, final Map map) {
        redisTemplate.opsForHash().putAll(key, map);

    }

    @Override
    public void delHashKeyMap(String key, String keyMap) {
        redisTemplate.opsForHash().delete(key, keyMap);
    }

    @Override
    public void putHashSession(final String key, final String keyMap, final Object obj) {
        redisTemplate.opsForHash().put(key, keyMap, obj);
    }

    @Override
    public Map getHashAllSession(final String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    @Override
    public Object getHashSession(final String key, final String mapKey) {
        return redisTemplate.opsForHash().get(key, mapKey);
    }

    @Override
    public void deleteSession(final String key) {
        //this.redisTemplate.delete(key);
        redisTemplate.execute(new RedisCallback<Object>() {
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                connection.del(redisTemplate.getStringSerializer().serialize(key));
                return null;
            }
        });
    }

    @Override
    public Object keys(final String key) {
        return redisTemplate.execute(new RedisCallback<Object>() {
            public Object doInRedis(RedisConnection connection) {
                Set<byte[]> set = connection.keys(redisTemplate.getStringSerializer().serialize(key));
                List<String> list = new ArrayList<>();
                for (byte[] by : set) {
                    list.add((String) redisTemplate.getStringSerializer().deserialize(by));
                }
                return list;
            }
        });
    }

    @Override
    public void leftPushList(String key, Object value) {
        redisTemplate.opsForList().leftPush(key, value);
    }

    @Override
    public void rightPushList(String key, Object value) {
        redisTemplate.opsForList().rightPush(key, value);
    }

    @Override
    public Object rightPopList(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    @Override
    public void rightPopAndLeftPush(String key1, String key2) {
        redisTemplate.opsForList().rightPopAndLeftPush(key1, key2);
    }

    @Override
    public void putRedis(final String key, final String value) {
        redisTemplate.execute(new RedisCallback<Object>() {
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                connection.set(redisTemplate.getStringSerializer().serialize(key), redisTemplate.getStringSerializer().serialize(value));
                return null;
            }
        });

    }

    @Override
    public String getRedis(final String keyValue) {
        return (String) redisTemplate.execute(new RedisCallback<Object>() {

            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] key = redisTemplate.getStringSerializer().serialize(keyValue);
                if (connection.exists(key)) {
                    byte[] value = connection.get(key);
                    return redisTemplate.getStringSerializer().deserialize(value);
                }
                return null;
            }
        });
    }

    @Override
    public void addExpire(String key, Integer time, TimeUnit timeUnit) {
        redisTemplate.expire(key, time, timeUnit);
    }

    @Override
    public void addExpireValue(String key, Object value, Integer time, TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, time, timeUnit);
    }

    @Override
    public Object getOpsForRedis(String key) {
        return redisTemplate.opsForValue().get(key);
    }


    /**
     * 添加缓存 保证原子性
     *
     * 如果为空就set值，并返回1
     * 如果存在(不为空)不进行操作，并返回0
     * 时间(秒) time要大于0 如果time小于等于0 将设置无限期(毫秒)
     * @return true成功 false失败
     */
    public boolean setIfAbsent(String key, Object value,long time) {
        try {
            return (Boolean) redisTemplate.execute(new RedisCallback<Object>() {
                @Override
                public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
                    return redisTemplate.opsForValue().setIfAbsent(key, value,time,TimeUnit.MILLISECONDS);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }
}
